package com.guigu.guliorder.service.impl;

import cn.hutool.core.lang.UUID;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guigu.gulicommon.constant.enumes.order.OrderStatus;
import com.guigu.gulicommon.context.UserContext;
import com.guigu.gulicommon.entity.dto.WareSkuLockDto;
import com.guigu.gulicommon.entity.pojo.Cart;
import com.guigu.gulicommon.entity.pojo.CartItem;
import com.guigu.gulicommon.entity.pojo.res.Res;
import com.guigu.gulicommon.entity.vo.*;
import com.guigu.gulicommon.exception.BaseException;
import com.guigu.guliorder.entity.OmsOrder;
import com.guigu.guliorder.entity.OmsOrderItem;
import com.guigu.guliorder.entity.dto.SubmitOrderDto;
import com.guigu.guliorder.entity.pojo.CreatOrderPoJo;
import com.guigu.guliorder.entity.vo.OrderConfirmsVo;
import com.guigu.guliorder.feign.CartServer;
import com.guigu.guliorder.feign.MemberServer;
import com.guigu.guliorder.feign.ProductServer;
import com.guigu.guliorder.feign.WareServer;
import com.guigu.guliorder.mapper.OmsOrderItemMapper;
import com.guigu.guliorder.mapper.OmsOrderMapper;
import com.guigu.guliorder.service.IOmsOrderService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

import static com.guigu.gulicommon.constant.RedisKey.*;
import static com.guigu.gulicommon.constant.enumes.IsDeleted.NOT_DELETED;
import static com.guigu.gulicommon.constant.enumes.res.OtherResCode.*;
import static com.guigu.gulicommon.constant.enumes.res.SelectResCode.*;
import static com.guigu.gulicommon.constant.enumes.res.ServerResCode.SERVER_BUSY;

/**
 * <p>
 * 订单 服务实现类
 * </p>
 *
 * @author 陈俊杰
 * @since 2024-08-15
 */
@Service
@Log4j2
public class OmsOrderServiceImpl extends ServiceImpl<OmsOrderMapper, OmsOrder> implements IOmsOrderService {

    @Autowired
    private WareServer wareServer;
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
    @Autowired
    private MemberServer memberServer;
    @Autowired
    private CartServer cartServer;
    @Autowired
    private ProductServer productServer;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private OmsOrderItemMapper omsOrderItemMapper;

    /**
     * 获取订单防冲令牌
     *
     * @return 订单防冲令牌
     */
    @Override
    public Res<String> getOrderToken() {
        //1 从本地线程变量中获取用户id
        Long userId = UserContext.getInstance().getCurrentUser().getId();
        //2 生成一个验证码,放入redis中
        //2.1 生成随机验证码
        String orderToken = UUID.randomUUID().toString().replace("-", "");
        //2.2 放入redis中
        redisTemplate.opsForValue().set(ORDER_TOKEN_PREFIX + userId, orderToken, ORDER_TOKEN_EXPIRE_TIME, ORDER_TOKEN_EXPIRE_TIME_UNIT);
        //3 返回给前端
        return new Res<>(GENERATION_ORDER_TOKEN_SUCCESS, orderToken);
    }

    /**
     * 提交订单
     *
     * @param dto 提交订单数据
     * @return 提交订单结果
     */
//    @GlobalTransactional
    @Transactional
    @Override
    public Res<CreatOrderPoJo> submitOrder(SubmitOrderDto dto) {
//        omsOrder.setPayType();
        //1 验证令牌的合法性
        String userId = UserContext.getInstance().getCurrentUser().getId().toString();
        if (!this.checkOrderToken(dto.getOrderToken(), ORDER_TOKEN_DISTRIBUTED_LOCK_PREFIX + userId)) {
            throw new BaseException(ORDER_TOKEN_ERROR);
        }
        //2 创建订单
        CreatOrderPoJo order = addOrder(dto.getAddressId());
        //3 锁定库存
        WareSkuLockDto lockDto = new WareSkuLockDto();
        lockDto.setOrderSn(order.getOrder().getOrderSn());
        List<CartItem> cartItems = order.getOrderItems().stream().map(o -> new CartItem().
                setSkuId(o.getSkuId())
                .setCount(Long.valueOf(o.getSkuQuantity()))
                .setTitle(o.getSkuName())).collect(Collectors.toList());
        lockDto.setLocks(cartItems);

        Res<LockStockVo> lockStockVoRes = wareServer.orderLockStock(lockDto);
        if (!lockStockVoRes.getCode().equals(LOCK_STOCK_SUCCESS.getCode())) {
            throw new BaseException(lockStockVoRes.getCode(), lockStockVoRes.getMsg());
        }
//        BeanUtils.copyProperties(order.getOrder(), omsOrder);
        return new Res<>(SUBMIT_ORDER_SUCCESS, order);
    }

    /**
     * 创建订单
     *
     * @param addrId 收货地址id
     * @return 订单信息
     */
    public CreatOrderPoJo addOrder(Long addrId) {

        CreatOrderPoJo orderPoJo = new CreatOrderPoJo();
        //1 生成订单号
        String orderSn = IdWorker.getTimeId();
        //2 获取收货信息
        Res<FareVo> fareVoRes = wareServer.getFareByAddrId(addrId);
        if (!fareVoRes.getCode().equals(SELECT_FARE_SUCCESS.getCode())) {
            throw new BaseException(fareVoRes.getCode(), fareVoRes.getMsg());
        }
        FareVo fareVo = fareVoRes.getData().get(0);
        //2.1 构建订单基本信息
        OmsOrder order = this.buildOrder(orderSn, fareVo);
        //2.2 保存订单
        order.setModifyTime(LocalDateTime.now());
        this.save(order);
        //3 获取购物车信息
        Res<Cart> cartRes = cartServer.getCartInfo();
        if (!cartRes.getCode().equals(SELECT_CART_INFO_SUCCESS.getCode())) {
            throw new BaseException(cartRes.getCode(), cartRes.getMsg());
        }

        //3.1 构建订单项
        List<OmsOrderItem> omsOrderItems = this.buildOrderItems(cartRes, orderSn);
        //3.2 保存订单项
        omsOrderItemMapper.insertBatch(omsOrderItems);

        //4 计算价格
        this.computePrice(omsOrderItems, order);
        orderPoJo.setOrder(order)
                .setOrderItems(omsOrderItems)
                .setPayPrice(order.getPayAmount())
                // 运费
                .setFare(fareVo.getFare())
        ;
        return orderPoJo;
    }

    /**
     * 计算价格
     *
     * @param orderItems 订单项
     * @param order      订单
     */
    public void computePrice(List<OmsOrderItem> orderItems, OmsOrder order) {

        BigDecimal total = new BigDecimal("0.0");
        BigDecimal coupon = new BigDecimal("0.0");
        BigDecimal integration = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");
        BigDecimal gift = new BigDecimal("0.0");
        BigDecimal growth = new BigDecimal("0.0");
        for (OmsOrderItem cartItem : orderItems) {
            coupon = coupon.add(cartItem.getCouponAmount());
            integration = integration.add(cartItem.getIntegrationAmount());
            promotion = promotion.add(cartItem.getPromotionAmount());
            total = total.add(cartItem.getRealAmount());
            gift = gift.add(new BigDecimal(cartItem.getGiftIntegration()));
            growth = growth.add(new BigDecimal(cartItem.getGiftGrowth()));
        }
        order.setTotalAmount(total)
                .setPayAmount(total.add(order.getFreightAmount()))
                .setPromotionAmount(promotion)
                .setIntegrationAmount(integration)
                .setIntegration(gift.intValue())
                .setGrowth(growth.intValue())
                .setCouponAmount(coupon);
    }

    /**
     * 构建订单项
     *
     * @param cartRes 购物车信息
     * @param orderSn 订单号
     * @return 订单项
     */
    public List<OmsOrderItem> buildOrderItems(Res<Cart> cartRes, String orderSn) {
        //1 远程调用获取商品SPU信息
        List<Long> skuIds = cartRes.getData().get(0).getCartItems().stream().map(CartItem::getSkuId).collect(Collectors.toList());
        Res<SpuInfoVo> spuInfoVoRes = productServer.getSpuInfoBySkuId(skuIds);
        if (!spuInfoVoRes.getCode().equals(SELECT_SPU_INFO_SUCCESS.getCode())) {
            throw new BaseException(spuInfoVoRes.getCode(), spuInfoVoRes.getMsg());
        }
//       // 2 封装为map,以spuId为key,SpuInfoVo为value
//        Map<Long, SpuInfoVo> spuInfoVoMap = spuInfoVoRes.getData().stream().collect(Collectors.toMap(
//                SpuInfoVo::getId,
//                spuInfoVo -> spuInfoVo
//        ));
        //2 获取商品信息集合
        List<SpuInfoVo> spuInfoVos = spuInfoVoRes.getData();

        //3 封装为订单项
        return cartRes.getData().get(0).getCartItems().stream()
                .map(cartItem -> {
                    //获取spu信息(集合中的第一个)
                    SpuInfoVo spuInfoVo = spuInfoVos.get(0);
                    OmsOrderItem item = new OmsOrderItem()
                            .setOrderSn(orderSn)
                            .setSpuId(spuInfoVo.getId())
                            .setSpuName(spuInfoVo.getSpuName())
                            .setSpuBrand(spuInfoVo.getBrandId().toString())
                            .setCategoryId(spuInfoVo.getCatalogId())
                            .setSkuId(cartItem.getSkuId())
                            .setSkuName(cartItem.getTitle())
                            .setSkuPic(cartItem.getImage())
                            .setSkuPrice(cartItem.getPrice())
                            .setSkuQuantity(Math.toIntExact(cartItem.getCount()))
                            .setSkuAttrsVals(StringUtils.collectionToDelimitedString(cartItem.getSkuAttrs(), ";"))
                            .setPromotionAmount(new BigDecimal("0"))
                            .setCouponAmount(new BigDecimal("0"))
                            .setIntegrationAmount(new BigDecimal("0"))
//                            .setRealAmount()
                            .setGiftIntegration(cartItem.getPrice().intValue())
                            .setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());
//                    BeanUtils.copyProperties(cartItem, item);
                    //减去各种优惠
                    BigDecimal multiply = item.getSkuPrice().multiply(new BigDecimal(item.getSkuQuantity()));
                    BigDecimal subtract = multiply.subtract(item.getCouponAmount()).subtract(item.getPromotionAmount()).subtract(item.getIntegrationAmount());
                    item.setRealAmount(subtract);
                    //设置完之后,删除第一项
                    spuInfoVos.remove(0);
                    return item;
                })
                .collect(Collectors.toList());
    }

    /**
     * 构建订单信息
     *
     * @param timeId 订单号
     * @param fareVo 运费信息
     * @return 订单信息
     */
    public OmsOrder buildOrder(String timeId, FareVo fareVo) {

        return new OmsOrder()
                .setMemberId(UserContext.getInstance().getCurrentUser().getId())
//                .setCouponId()
//                .setCreateTime()
//                .setMemberUsername()
//                .setTotalAmount()
//                .setPayAmount()
//                .setId()
                .setOrderSn(timeId)
                .setFreightAmount(fareVo.getFare())
//                .setPromotionAmount()
//                .setIntegrationAmount()
//                .setCouponAmount()
//                .setDiscountAmount()
//                .setPayType()
//                .setSourceType()
                .setStatus(OrderStatus.WAIT_PAYMENT)
//                .setDeliveryCompany()
//                .setDeliverySn()
                .setAutoConfirmDay(7)
//                .setIntegration()
//                .setGrowth()
//                .setBillType()
//                .setBillHeader()
//                .setBillContent()
//                .setBillReceiverPhone()
//                .setBillReceiverEmail()
                .setReceiverName(fareVo.getAddress().getName())
                .setReceiverPhone(fareVo.getAddress().getPhone())
                .setReceiverPostCode(fareVo.getAddress().getPostCode())
                .setReceiverProvince(fareVo.getAddress().getProvince())
                .setReceiverCity(fareVo.getAddress().getCity())
                .setReceiverRegion(fareVo.getAddress().getRegion())
                .setReceiverDetailAddress(fareVo.getAddress().getDetailAddress())
//                .setNote()
//                .setConfirmStatus()
                .setDeleteStatus(NOT_DELETED);
    }


    /**
     * 校验防冲令牌
     *
     * @param uToken  用户页面传过来的令牌
     * @param lockKey 分布式锁的key
     * @return 是否通过验证
     */
    @Lock4j(keys = {"#lockKey"}, acquireTimeout = ORDER_TOKEN_DISTRIBUTED_LOCK_ACQUIRE_TIMEOUT, expire = ORDER_TOKEN_DISTRIBUTED_LOCK_EXPIRE)
    public boolean checkOrderToken(String uToken, String lockKey) {
        log.info("开启分布式锁");
        //1 从redis中获取token
        String userId = UserContext.getInstance().getCurrentUser().getId().toString();
        String rToken = String.valueOf(redisTemplate.opsForValue().get(ORDER_TOKEN_PREFIX + userId));
        //2 比较
        if (rToken != null && rToken.equals(uToken)) {
            //如果相同,则删除redis中的token
            redisTemplate.delete(ORDER_TOKEN_PREFIX + UserContext.getInstance().getCurrentUser().getId());
            return true;
        } else {
            return false;
        }
    }


    /**
     * 获取订单详情
     */
    @Override
    public Res<OrderConfirmsVo> getOrderDetail() {
        //1 生成防冲令牌
        String token = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(ORDER_TOKEN_PREFIX + UserContext.getInstance().getCurrentUser().getId(), token, ORDER_TOKEN_EXPIRE_TIME, ORDER_TOKEN_EXPIRE_TIME_UNIT);
        OrderConfirmsVo vo = new OrderConfirmsVo()
                .setOrderToken(token);
        //2 获取购物车列表中的购物项
        CompletableFuture<Void> cartFuture = CompletableFuture.supplyAsync(() -> {
            //2.1 远程调用购物车服务
            Res<Cart> cartRes = cartServer.getCartInfo();
            if (!cartRes.getCode().equals(SELECT_CART_INFO_SUCCESS.getCode())) {
                throw new BaseException(cartRes.getCode(), cartRes.getMsg());
            }
            //2.2 获取购物车列表
            //2.3 获取商品Id列表
            return cartRes.getData().get(0).getCartItems();
        }, threadPoolTaskExecutor).thenApply(cartItems -> {
            List<Long> skuIds = cartItems.stream().map(CartItem::getSkuId).collect(Collectors.toList());
            //2.4 调用远程服务查询商品详情
            Res<SkuInfoPageVo> skuInfos = productServer.getListByIds(skuIds);
            if (!skuInfos.getCode().equals(SEARCH_PRODUCT_SUCCESS.getCode())) {
                throw new BaseException(skuInfos.getCode(), skuInfos.getMsg());
            }
            //2.5 找到到对应商品,重新设置价格
            cartItems.forEach(cartItem -> {
                skuInfos.getData().stream().filter(i -> i.getSkuId().equals(cartItem.getSkuId())).findFirst().ifPresent(skuInfo -> cartItem.setPrice(skuInfo.getPrice()));
            });
            vo.setCartItems(cartItems);
            return skuIds;
        }).thenAccept(skuIds -> {
            //2.6 查询库存
            Res<HasStockSkuVo> stockBySkuId = wareServer.getStockBySkuId(skuIds);
            if (!stockBySkuId.getCode().equals(SELECT_SKU_STOCK_SUCCESS.getCode())) {
                throw new BaseException(stockBySkuId.getCode(), stockBySkuId.getMsg());
            }
            //2.7 找到到对应商品,重新设置库存
            stockBySkuId.getData().forEach(hasStockSkuVo -> {
                vo.getCartItems().stream().filter(cartItem -> cartItem.getSkuId().equals(hasStockSkuVo.getSkuId())).findFirst().ifPresent(cartItem -> cartItem.setHasStock(hasStockSkuVo.getHasStock()));
            });
        });


        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
            //3 获取地址列表
            Res<MemberAddressVo> memberAddressVoRes = memberServer.list();
            if (!memberAddressVoRes.getCode().equals(SELECT_MEMBER_RECEIVE_ADDRESS_LIST_SUCCESS.getCode())) {
                throw new BaseException(memberAddressVoRes.getCode(), memberAddressVoRes.getMsg());
            }
            //3.2 获取会员收货地址列表
            List<MemberAddressVo> memberAddress = memberAddressVoRes.getData();
            vo.setMemberAddress(memberAddress);

        }, threadPoolTaskExecutor);


        CompletableFuture<Void> integrationFuture = CompletableFuture.runAsync(() -> {
            //4. 获取用户积分
            Res<UserInfoVo> userInfoVoRes = memberServer.getById();
            if (!userInfoVoRes.getCode().equals(SELECT_MEMBER_RECEIVE_ADDRESS_LIST_SUCCESS.getCode())) {
                throw new BaseException(userInfoVoRes.getCode(), userInfoVoRes.getMsg());
            }
            //4.2 获取用户积分
            Integer integration = userInfoVoRes.getData().get(0).getIntegration();
            vo.setIntegration(integration);
        }, threadPoolTaskExecutor);

        try {
            CompletableFuture.allOf(cartFuture, addressFuture, integrationFuture).get();
        } catch (InterruptedException | ExecutionException e) {
            log.error("服务远程调用失败,原因是:", e);
            throw new BaseException(SERVER_BUSY);
        }
        return new Res<>(SELECT_ORDER_DETAIL_SUCCESS, vo);
    }

    /**
     * 根据订单号获取订单状态
     *
     * @param orderSn 订单号
     * @return 订单状态
     */
    @Override
    public Res<OmsOrderVo> getOrderStatusBySn(String orderSn) {
        OmsOrder omsOrder = new LambdaQueryChainWrapper<>(baseMapper).eq(OmsOrder::getOrderSn, orderSn).one();
//        if (omsOrder == null) {
//            throw new BaseException(ORDER_NOT_EXIST);
//        }
        OmsOrderVo omsOrderVo = new OmsOrderVo();
        BeanUtils.copyProperties(omsOrder, omsOrderVo);

        return new Res<>(SELECT_ORDER_DETAIL_SUCCESS, omsOrderVo);
    }


}
